home *** CD-ROM | disk | FTP | other *** search
/ Sprite 1984 - 1993 / Sprite 1984 - 1993.iso / src / lib / c / etc / option.c < prev    next >
C/C++ Source or Header  |  1991-01-28  |  11KB  |  449 lines

  1. /*
  2.  * option.c --
  3.  *
  4.  *    Routines to do command line option processing.
  5.  *
  6.  * Copyright 1986, 1991 Regents of the University of California
  7.  * Permission to use, copy, modify, and distribute this
  8.  * software and its documentation for any purpose and without
  9.  * fee is hereby granted, provided that the above copyright
  10.  * notice appear in all copies.  The University of California
  11.  * makes no representations about the suitability of this
  12.  * software for any purpose.  It is provided "as is" without
  13.  * express or implied warranty.
  14.  */
  15.  
  16. #ifndef lint
  17. static char rcsid[] = "$Header: /sprite/src/lib/c/etc/RCS/option.c,v 1.9 91/01/28 16:44:36 kupfer Exp $ SPRITE (Berkeley)";
  18. #endif not lint
  19.  
  20. #include <option.h>
  21. #include <cfuncproto.h>
  22. #include <stdio.h>
  23. #include <stdlib.h>
  24. #include <strings.h>
  25. #include <time.h>
  26.  
  27. #define OptNoArg(progName, opt) fprintf(stderr, \
  28.               "Warning: %s option \"-%s\" needs an argument\n", \
  29.               (progName), (opt))
  30.  
  31. /* Forward references: */
  32.  
  33. static void ParseTime _ARGS_ ((char *progName, char *str,
  34.                  time_t *resultPtr));
  35.  
  36.  
  37. /*
  38.  *----------------------------------------------------------------------
  39.  *
  40.  * Opt_Parse --
  41.  *
  42.  *    Process a command line according to a template of accepted
  43.  *    options.  See the manual page and header file for more details.
  44.  *
  45.  * Results:
  46.  *    The number of options that weren't processed by this procedure
  47.  *    is returned, and argv points to an array of unprocessed
  48.  *    options.  (This is all of the options that didn't start with
  49.  *    "-", except for those used as arguments to the options
  50.  *    processed here; it's also anything after an OPT_REST option.)
  51.  *
  52.  * Side effects:
  53.  *    The variables referenced from the option array get modified
  54.  *    if their option was present on the command line.  Can clobber 
  55.  *    the global buffer used by localtime(3).
  56.  *
  57.  *----------------------------------------------------------------------
  58.  */
  59.  
  60. int
  61. Opt_Parse(argc, argv, optionArray, numOptions, flags)
  62.     register int  argc;         /* Number of arguments in argv. */
  63.     char          **argv;           /* Array of arguments */
  64.     Option        optionArray[];    /* Array of option descriptions */
  65.     int              numOptions;        /* Size of optionArray */
  66.     int          flags;        /* Or'ed combination of various flag bits:
  67.                      * see option.h for definitions. */
  68. {
  69.     register Option     *optionPtr; /* pointer to the current option in the
  70.                      * array of option specifications */
  71.     register char     *curOpt;    /* Current flag argument */
  72.     register char     **curArg;   /* Current argument */
  73.     register int      argIndex;   /* Index into argv to which next unused
  74.                      * argument should be copied */
  75.     int           stop=0;        /* Set non-zero to stop processing
  76.                      * arguments when an OPT_REST flag is
  77.                      * encountered */
  78.     int            length;        /* Number of characters in current
  79.                      * option. */
  80.  
  81.     argIndex = 1;
  82.     argc -= 1;
  83.     curArg = &argv[1];
  84.  
  85.     while (argc && !stop) {
  86.     if (**curArg == '-') {
  87.         curOpt = &curArg[0][1];
  88.         curArg += 1;
  89.         argc -= 1;
  90.  
  91.         /*
  92.          * Check for the special options "?" and "help".  If found,
  93.          * print documentation and exit.
  94.          */
  95.  
  96.         if ((strcmp(curOpt, "?") == 0) || (strcmp(curOpt, "help") == 0)) {
  97.         Opt_PrintUsage (argv[0], optionArray, numOptions);
  98.         exit(0);
  99.         }
  100.  
  101.         /*
  102.          * Loop over all the options specified in a single argument
  103.          * (must be 1 unless OPT_ALLOW_CLUSTERING was specified).
  104.          */
  105.  
  106.         while (1) {
  107.         /*
  108.          * Loop over the array of options searching for one with the
  109.          * matching key string.  If found, it is left pointed to by
  110.          * optionPtr.
  111.          */
  112.         for (optionPtr = &optionArray[numOptions - 1];
  113.             optionPtr >= optionArray;
  114.             optionPtr -= 1) {
  115.              if (optionPtr->key == NULL) {
  116.              continue;
  117.              }
  118.              if (*optionPtr->key == *curOpt) {
  119.              if (flags & OPT_ALLOW_CLUSTERING) {
  120.                  length = strlen(optionPtr->key);
  121.                  if (strncmp(optionPtr->key, curOpt, length) == 0) {
  122.                  break;
  123.                  }
  124.              } else {
  125.                  if (strcmp(optionPtr->key, curOpt) == 0) {
  126.                  break;
  127.                  }
  128.              }
  129.              }
  130.         }
  131.  
  132.         if (optionPtr < optionArray) {
  133.             /*
  134.              * No match.  Print error message and skip option.
  135.              */
  136.  
  137.             fprintf(stderr, "Unknown option \"-%s\";", curOpt);
  138.             fprintf(stderr, "  type \"%s -help\" for information\n",
  139.                 argv[0]);
  140.             break;
  141.         }
  142.  
  143.         /*
  144.          * Take the appropriate action based on the option type
  145.          */
  146.  
  147.         if (optionPtr->type >= 0) {
  148.             *((int *) optionPtr->address) = optionPtr->type;
  149.         } else {
  150.             switch (optionPtr->type) {
  151.             case OPT_REST:
  152.                 stop = 1;
  153.                 *((int *) optionPtr->address) = argIndex;
  154.                 break;
  155.             case OPT_STRING:
  156.                 if (argc == 0) {
  157.                 OptNoArg(argv[0], optionPtr->key);
  158.                 } else {
  159.                 *((char **)optionPtr->address) = *curArg;
  160.                 curArg++;
  161.                 argc--;
  162.                 }
  163.                 break;
  164.             case OPT_INT:
  165.                 if (argc == 0) {
  166.                 OptNoArg(argv[0], optionPtr->key);
  167.                 } else {
  168.                 char *endPtr;
  169.  
  170.                 *((int *) optionPtr->address) =
  171.                     strtol(*curArg, &endPtr, 0);
  172.                 if (endPtr == *curArg) {
  173.                     fprintf(stderr,
  174.       "Warning: option \"-%s\" got a non-numeric argument \"%s\".  Setting to 0.\n",
  175.                         optionPtr->key, *curArg);
  176.                 }
  177.                 curArg++;
  178.                 argc--;
  179.                 }
  180.                 break;
  181.             case OPT_TIME:
  182.                 if (argc == 0) {
  183.                 OptNoArg(argv[0], optionPtr->key);
  184.                 } else {
  185.                 ParseTime(argv[0], *curArg, 
  186.                       (time_t *)optionPtr->address);
  187.                 curArg++;
  188.                 argc--;
  189.                 }
  190.                 break;
  191.             case OPT_FLOAT:
  192.                 if (argc == 0) {
  193.                 OptNoArg(argv[0], optionPtr->key);
  194.                 } else {
  195.                 char *endPtr;
  196.  
  197.                 *((double *) optionPtr->address) =
  198.                     strtod(*curArg, &endPtr);
  199.                 if (endPtr == *curArg) {
  200.                     fprintf(stderr,
  201.       "Warning: option \"-%s\" got non-floating-point argument \"%s\".  Setting to 0.\n",
  202.                         optionPtr->key, *curArg);
  203.                 }
  204.                 curArg++;
  205.                 argc--;
  206.                 }
  207.                 break;
  208.             case OPT_GENFUNC: {
  209.                 int        (*handlerProc)();
  210.  
  211.                 handlerProc = (int (*)())optionPtr->address;
  212.  
  213.                 argc = (* handlerProc) (optionPtr->key, argc,
  214.                     curArg);
  215.                 break;
  216.             }
  217.             case OPT_FUNC: {
  218.                 int (*handlerProc)();
  219.  
  220.                 handlerProc = (int (*)())optionPtr->address;
  221.                 
  222.                 if ((* handlerProc) (optionPtr->key, *curArg)) {
  223.                 curArg += 1;
  224.                 argc -= 1;
  225.                 }
  226.                 break;
  227.             }
  228.             case OPT_DOC:
  229.                 Opt_PrintUsage (argv[0], optionArray, numOptions);
  230.                 exit(0);
  231.                 /*NOTREACHED*/
  232.             }
  233.         }
  234.         /*
  235.          * Advance to next option
  236.          */
  237.  
  238.         if (flags & OPT_ALLOW_CLUSTERING) {
  239.             curOpt += length;
  240.             if (*curOpt == 0) {
  241.             break;
  242.             }
  243.         } else {
  244.             break;
  245.         }
  246.         }
  247.     } else {
  248.         /*
  249.          * *curArg is an argument for which we have no use, so copy it
  250.          * down.
  251.          */
  252.         argv[argIndex] = *curArg;
  253.         argIndex += 1;
  254.         curArg += 1;
  255.         argc -= 1;
  256.  
  257.         /*
  258.          * If this wasn't an option, and we're supposed to stop parsing
  259.          * the first time we see something other than "-", quit.
  260.          */
  261.         if (flags & OPT_OPTIONS_FIRST) {
  262.         stop = 1;
  263.         }
  264.     }
  265.     }
  266.  
  267.     /*
  268.      * If we broke out of the loop because of an OPT_REST argument, we want
  269.      * to copy the rest of the arguments down, so we do.
  270.      */
  271.     while (argc) {
  272.     argv[argIndex] = *curArg;
  273.     argIndex += 1;
  274.     curArg += 1;
  275.     argc -= 1;
  276.     }
  277.     argv[argIndex] = (char *)NULL;
  278.     return argIndex;
  279. }
  280.  
  281.  
  282. /*
  283.  *----------------------------------------------------------------------
  284.  *
  285.  * Opt_PrintUsage --
  286.  *
  287.  *    Print out a usage message for a command.  This prints out the
  288.  *    documentation strings associated with each option.
  289.  *
  290.  * Results:
  291.  *    none.
  292.  *
  293.  * Side effects:
  294.  *    Messages printed onto the console.
  295.  *
  296.  *----------------------------------------------------------------------
  297.  */
  298.  
  299. void
  300. Opt_PrintUsage(commandName, optionArray, numOptions)
  301.     char *commandName;
  302.     Option optionArray[];
  303.     int numOptions;
  304. {
  305.     register int i;
  306.     int width;
  307.  
  308.     /*
  309.      * First, compute the width of the widest option key, so that we
  310.      * can make everything line up.
  311.      */
  312.  
  313.     width = 4;
  314.     for (i=0; i<numOptions; i++) {
  315.     int length;
  316.     if (optionArray[i].key == NULL) {
  317.         continue;
  318.     }
  319.     length = strlen(optionArray[i].key);
  320.     if (length > width) {
  321.         width = length;
  322.     }
  323.     }
  324.  
  325.     fprintf(stderr, "Usage of command \"%s\"\n", commandName);
  326.     for (i=0; i<numOptions; i++) {
  327.     if (optionArray[i].type != OPT_DOC) {
  328.         fprintf(stderr, " -%s%-*s %s\n", optionArray[i].key,
  329.             width+1-strlen(optionArray[i].key), ":",
  330.             optionArray[i].docMsg);
  331.         switch (optionArray[i].type) {
  332.         case OPT_INT: {
  333.             fprintf(stderr, "\t\tDefault value: %d\n",
  334.                 *((int *) optionArray[i].address));
  335.             break;
  336.         }
  337.         case OPT_FLOAT: {
  338.             fprintf(stderr, "\t\tDefault value: %lg\n",
  339.                 *((double *) optionArray[i].address));
  340.             break;
  341.         }
  342.         case OPT_STRING: {
  343.             if (*(char **)optionArray[i].address != (char *) NULL) {
  344.                 fprintf(stderr, "\t\tDefault value: \"%s\"\n",
  345.                     *(char **) optionArray[i].address);
  346.                 break;
  347.             }
  348.         }
  349.         default: {
  350.             break;
  351.         }
  352.         }
  353.     } else {
  354.         fprintf(stderr, " %s\n", optionArray[i].docMsg);
  355.     }
  356.     }
  357.     fprintf(stderr, " -help%-*s Print this message\n", width-3, ":");
  358. }
  359.  
  360.  
  361. /*
  362.  *----------------------------------------------------------------------
  363.  *
  364.  * ParseTime --
  365.  *
  366.  *    Convert a date and time from some string representation to 
  367.  *    something we can compute with.
  368.  *
  369.  * Results:
  370.  *    If str points to a parsable time, the corresponding UNIX time 
  371.  *    value (seconds past the epoch) is returned through resultPtr.
  372.  *
  373.  * Side effects:
  374.  *    Can clobber the global buffer used by localtime(3).
  375.  *
  376.  *----------------------------------------------------------------------
  377.  */
  378.  
  379. static void
  380. ParseTime(progName, str, resultPtr)
  381.     char    *progName;    /* name that the program was called as */
  382.     char    *str;        /* the string to parse */
  383.     time_t    *resultPtr;    /* pointer to result time value */
  384. {
  385.     long result;        /* the answer */
  386.     char *endPtr;        /* pointer into str, for parsing */
  387.     struct tm pieces;        /* year, month, etc. as integers */
  388.  
  389.     /* 
  390.      * We currently accept the following formats:
  391.      * 
  392.      * (1) an integer number of seconds past the epoch.
  393.      * (2) a string of the form "yy.mm.dd.hh.mm.ss"
  394.      */
  395.     
  396.     result = strtol(str, &endPtr, 0);
  397.     if (endPtr == str) {
  398.     goto parseError;
  399.     }
  400.     if (*endPtr == '\0') {
  401.     *resultPtr = result;
  402.     return;
  403.     }
  404.  
  405.     /* 
  406.      * Not a simple integer, so try form 2. 
  407.      */
  408.     if (*endPtr != '.') {
  409.     goto parseError;
  410.     }
  411.     pieces.tm_year = result;
  412.     if (pieces.tm_year > 1900) {
  413.     pieces.tm_year -= 1900;
  414.     }
  415.     pieces.tm_mon = strtol(endPtr+1, &endPtr, 0) - 1;
  416.     if (endPtr == str || *endPtr != '.') {
  417.     goto parseError;
  418.     }
  419.     pieces.tm_mday = strtol(endPtr+1, &endPtr, 0);
  420.     if (endPtr == str || *endPtr != '.') {
  421.     goto parseError;
  422.     }
  423.     pieces.tm_hour = strtol(endPtr+1, &endPtr, 0);
  424.     if (endPtr == str || *endPtr != '.') {
  425.     goto parseError;
  426.     }
  427.     pieces.tm_min = strtol(endPtr+1, &endPtr, 0);
  428.     if (endPtr == str || *endPtr != '.') {
  429.     goto parseError;
  430.     }
  431.     pieces.tm_sec = strtol(endPtr+1, &endPtr, 0);
  432.     if (endPtr == str || *endPtr != '\0') {
  433.     goto parseError;
  434.     }
  435.  
  436.     result = mktime(&pieces);
  437.     if (result == -1) {
  438.     fprintf(stderr, "%s: can't represent the time \"%s\".\n",
  439.         progName, str);
  440.     } else {
  441.     *resultPtr = result;
  442.     }
  443.     return;
  444.  
  445.  parseError:
  446.     fprintf(stderr, "%s: can't parse \"%s\" as a time.\n", progName, str);
  447.     return;
  448. }
  449.